﻿using System;
using System.IO;
using System.Text;
using Jamhee.Noctiluca.Common;
using Jamhee.Noctiluca.LightWiki.PropertyFiles;
using Jamhee.Noctiluca.LightWiki.Markup;
using Jamhee.Noctiluca.WebServer;
using System.Net;
using System.Web;
using System.Collections.Generic;
using System.Collections.Specialized;

namespace Jamhee.Noctiluca.LightWiki.Pages
{
    class WikiPageHandler : TextPageHandler
    {
        private WikiMarkupParser _wikiMarkupParser = new WikiMarkupParser();

        #region Constants

        private readonly string TEMPLATE_ADDPAGE = AppUtil.WORK_ROOT + @"\doc\wiki\themes\default\pages\addpage.template";
        private readonly string TEMPLATE_EDITPAGE = AppUtil.WORK_ROOT + @"\doc\wiki\themes\default\pages\editpage.template";
        private readonly string TEMPLATE_VIEWPAGE = AppUtil.WORK_ROOT + @"\doc\wiki\themes\default\pages\viewpage.template";

        private const string WIKI_BODY = "$(WIKI_BODY)";
        private const string WIKI_PAGE_FOLDER_NAME = "$(WIKI_PAGE_FOLDER_NAME)";
        private const string WIKI_CHILDPAGE = "$(WIKI_CHILDPAGE)";
        private const string WIKI_CHILDPAGE_LABEL_FLAG = "$(WIKI_CHILDPAGE_LABEL_FLAG)";
        private const string WIKI_SITE_LOGO = "$(WIKI_SITE_LOGO)";
        private const string WIKI_SITE_NAME = "$(WIKI_SITE_NAME)";
        private const string WIKI_SITE_COPYRIGHT = "$(WIKI_COPYRIGHT)";

        private const string SAVEPAGE_EDIT_TITLE = "pagetitle";
        private const string SAVEPAGE_EDIT_CONTENT = "pagecontent";
        private const string SAVEPAGE_ALL_SOURCEPAGE = "sourcepage";
        private const string SAVEPAGE_ALL_SOURCEPAGE_ADDPAGE = "addpage";
        private const string SAVEPAGE_ADD_URL = "url";

        #endregion

        #region View Page

        protected override string ReplacePageVariable(string docPath, string line, WikiPageInfo pageInfo, bool parseWikiMarkup, Uri url)
        {
            FileInfo fi = new FileInfo(docPath);

            // Replace the title
            if (line.IndexOf(WikiConstants.WIKI_TITLE) >= 0)
            {
                line = line.Replace(WikiConstants.WIKI_TITLE, pageInfo.GetPageTitle());
            }

            // Replace the PAGE FOLDER NAME
            if (line.IndexOf(WIKI_PAGE_FOLDER_NAME) >= 0)
            {
                line = line.Replace(WIKI_PAGE_FOLDER_NAME, fi.Directory.Name);
            }

            // Replace the body
            if (line.IndexOf(WIKI_BODY) >= 0)
            {
                string wikiContent = GetWikiContent(docPath, url, parseWikiMarkup);
                line = line.Replace(WIKI_BODY, wikiContent);
            }

            // Replace the child pages
            string childPage = GetChildPages(fi, url);
            if (line.IndexOf(WIKI_CHILDPAGE) >= 0)
            {
                line = line.Replace(WIKI_CHILDPAGE, childPage);
            }

            if (line.IndexOf(WIKI_CHILDPAGE_LABEL_FLAG) >= 0)
            {
                if (childPage.Length > 0)
                {
                    line = line.Replace(WIKI_CHILDPAGE_LABEL_FLAG, "block");
                }
                else
                {
                    line = line.Replace(WIKI_CHILDPAGE_LABEL_FLAG, "none");
                }
            }

            return line;
        }

        private string GetWikiContent(string contentDocPath, Uri url, bool parseWikiMarkup)
        {
            StreamReader sReader = null;
            StringWriter sWriter = null;
            StringBuilder sBuilder = new StringBuilder();

            try
            {
                sReader = new StreamReader(contentDocPath, Encoding.Default);
                sWriter = new StringWriter(sBuilder);

                //Clear the stack cache of Wikimarkup Parser
                _wikiMarkupParser.ClearStack();

                string input = null;
                while ((input = sReader.ReadLine()) != null)
                {
                    // For the wiki content, we need to convert current input to an html-encode string
                    input = HttpUtility.HtmlEncode(input);

                    // Get document folder name, it will be used to retrieve attachments and child page links of this document
                    string docFolderName = Directory.GetParent(contentDocPath).Name;
                    if (parseWikiMarkup)
                    {
                        input = _wikiMarkupParser.Parse(input, docFolderName, url);
                    }

                    sWriter.WriteLine(input);
                }

                sWriter.Flush();

            }
            catch (Exception e)
            {
                LogUtil.PrintError(e.Message);
            }
            finally
            {
                if (sReader != null)
                {
                    sReader.Close();
                }

                if (sWriter != null)
                {
                    sWriter.Close();
                }
            }

            return sBuilder.ToString();
        }

        protected string GetChildPages(FileInfo contentFile, Uri url)
        {
            DirectoryInfo currentDir = contentFile.Directory;
            List<ChildPage> childPages = new List<ChildPage>();

            // Get all child pages
            foreach (DirectoryInfo dir in currentDir.GetDirectories())
            {
                // exclude the subfolders such as "_attachment" and ".svn"
                if (!dir.Name.StartsWith("_") && !dir.Name.StartsWith("."))
                {
                    WikiPageInfo pageInfo = new WikiPageInfo(dir.FullName + "\\" + WikiConstants.WIKI_DOC_INFO);
                    string pageName = dir.Name;
                    if (pageInfo.GetPageTitle() != null && pageInfo.GetPageTitle().Trim().Length > 0)
                    {
                        pageName = pageInfo.GetPageTitle();
                    }

                    ChildPage childPage = new ChildPage(dir.Name, pageName);
                    childPages.Add(childPage);
                }
            }

            // Output the html string
            childPages.Sort(new ChildPageComparor());
            string html = "";

            for (int index = 0; index < childPages.Count; index++)
            {
                html += "<a href=\"" + url.AbsolutePath.TrimEnd('/') + "/" + childPages[index].FolderName +"\">" + childPages[index].PageName + "</a><br>";
            }

            return html;
        }

        protected override string GetPageTemplateName(UrlQueryResult.ActionType action)
        {
            if (action == UrlQueryResult.ActionType.Add)
            {
                return TEMPLATE_ADDPAGE;
            }
            else if (action == UrlQueryResult.ActionType.Edit)
            {
                return TEMPLATE_EDITPAGE;
            }
            else
            {
                return TEMPLATE_VIEWPAGE;
            }
        }

        protected override bool HyperLinkForCurrentPageInNavigator
        {
            get { return false; }
        }

        class ChildPage
        {
            string _folderName;
            string _pageName;

            public ChildPage(string folderName, string pageName)
            {
                _folderName = folderName;
                _pageName = pageName;
            }

            public string FolderName
            {
                get { return _folderName; }
            }

            public string PageName
            {
                get { return _pageName; }
            }
        }

        class ChildPageComparor : IComparer<ChildPage>
        {
            #region IComparer Members

            public int Compare(ChildPage x, ChildPage y)
            {
                return x.PageName.CompareTo(y.PageName);
            }

            #endregion
        }



        #endregion

        #region Save Page

        public override void Save(string contentDocPath, HttpListenerContext context,
            IWebConfiguration webConfig)
        {
            Stream input = context.Request.InputStream;
            StreamReader sr = new StreamReader(input);

            string redirectURL = null;

            try
            {
                FileInfo fi = new FileInfo(contentDocPath);
                WikiPageInfo pageInfo = new WikiPageInfo(Path.GetFullPath(fi.DirectoryName + "\\" + WikiConstants.WIKI_DOC_INFO));

                string line = null;
                NameValueCollection body = null;

                if ((line = sr.ReadLine()) != null)
                {
                    body = HttpUtility.ParseQueryString(line);

                    // Get source page
                    string sourcePage = body.Get(SAVEPAGE_ALL_SOURCEPAGE);
                    if (sourcePage != null && sourcePage.Equals(SAVEPAGE_ALL_SOURCEPAGE_ADDPAGE))
                    {
                        //====== The source page is ADD PAGE ======
                        
                        // Get page name
                        string url = body.Get(SAVEPAGE_ADD_URL);
                        if (url != null && url.Length > 0)
                        {
                            
                            string[] potentialPaths = WikiUtils.GetDocument(new Uri(url), webConfig, WikiUtils.DocType.WikiContent).Split('|');

                            for (int index = 0; index < potentialPaths.Length; index++)
                            {
                                FileInfo potentialContentFile = new FileInfo(potentialPaths[index]);

                                // If parent directory exists, create the folder; otherwise, check the remaining potential folders
                                if (potentialContentFile.Directory.Parent.Exists)
                                {
                                    if (!potentialContentFile.Directory.Exists)
                                    {
                                        //TODO: We need to add a logic to handle the potential exception
                                        potentialContentFile.Directory.Create();
                                    }

                                    if (!potentialContentFile.Exists)
                                    {
                                        //TODO: We need to add a logic to handle the potential exception
                                        potentialContentFile.Create();
                                    }

                                    FileInfo wikiInfoFile = new FileInfo(potentialContentFile.Directory.FullName + "\\" + 
                                        WikiConstants.WIKI_DOC_INFO);
                                    if (!wikiInfoFile.Exists)
                                    {
                                        //TODO: We need to add a logic to handle the potential exception
                                        wikiInfoFile.Create();
                                    }

                                    redirectURL = url + "?type=wiki&action=edit";
                                    break;
                                }
                            }
                        }
                    }
                    else
                    {
                        //====== The source page is EDIT PAGE ======

                        // Get page title
                        string title = body.Get(SAVEPAGE_EDIT_TITLE);
                        if (title != null)
                        {
                            pageInfo.SetPageTitle(HttpUtility.UrlDecode(title));
                        }

                        // Get page content
                        string content = body.Get(SAVEPAGE_EDIT_CONTENT);
                        if (content != null)
                        {
                            //TODO: We need to add a logic to handle the situation if this file is in use or catch the exception temporarily
                            File.WriteAllText(contentDocPath, content, Encoding.Default);
                        }
                    }
                }

            }
            catch (Exception e)
            {
                LogUtil.PrintError(e.Message);
                throw e;
            }
            finally
            {
                if (sr != null)
                {
                    sr.Close();
                }
            }

            if (redirectURL == null)
            {
                redirectURL = "?type=wiki&action=view";
            }

            context.Response.Redirect(redirectURL);
            context.Response.Close();
        }

        #endregion

        #region Download Page

        public override void Download(string contentDocPath, HttpListenerContext context,
            IWebConfiguration webConfig)
        {
            // Regard downloading as viewing operation for wiki page
            ParseDoc(contentDocPath, context, webConfig, UrlQueryResult.ActionType.View);
        }

        #endregion


        #region Delete Page

        public override void Delete(string contentDocPath, HttpListenerContext context,
            IWebConfiguration webConfig)
        {
            // Regard deleting as viewing operation for wiki page
            ParseDoc(contentDocPath, context, webConfig, UrlQueryResult.ActionType.View);
        }

        #endregion
    }
}
